/*******************************************************************************
 * Copyright (c) 2010, 2010 Xbeam Project and ChenZhiKong.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 *******************************************************************************/
package org.bellusoft.xbeam.framework;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import org.bellusoft.xbeam.contract.concurrent.ThreadSafe;
import org.bellusoft.xbeam.framework.module.StaticHost;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;

@ThreadSafe
class BundleImpl implements Bundle {
	
	private BundleStorage storage ; 
	
	private BundleContext context ; 
	
	private final ReentrantLock lock = new ReentrantLock(); 
	
	private Condition startedAndStoppedCondition ;
	
	/* only used when state is starting, mark whether bundle is lazy started*/
	private boolean lazyStarting = false ; 

	private int state ; 
	
	private BundleActivator activator ; 
	
	private final List<Module> obsoleteHosts = new ArrayList<Module>(0);
	
	private Module hostBundle ;

	public int compareTo(Bundle o) {
		return (int)(getBundleId()-o.getBundleId());
	}

	public int getState() {
		lock.lock();
		try{
			return state;
		}finally{
			lock.unlock() ; 
		}
	}

	public void start(int options) throws BundleException {
		/**
		 * p 6.1.4.31
		 */
		if(state==Bundle.UNINSTALLED){
			throw new IllegalStateException("uninstalled bundle cannot be started");
		}
		if(startLevelStatisfied()){
			if((((state & Bundle.STARTING)!=0)&&(!lazyStarting))||
					((state & Bundle.STOPPING)!=0)){
				boolean timeout = false ; 
				try{
					//TODO： need wait in a while ? 
					timeout = startedAndStoppedCondition.await(30000, TimeUnit.MILLISECONDS);
				}catch (InterruptedException e) {
					// TODO: handle exception
				}
				if(timeout){
					throw new BundleException("Bundle start time out.");
				}
			}
			if( (state & Bundle.ACTIVE) != 0){
				return ; 
			}
			if((options & Bundle.START_TRANSIENT)!=0){
				if((options&Bundle.START_ACTIVATION_POLICY)!=0){
					storage.setStartSetting(AutoStartSetting.DeclaredActive);
				}else{
					storage.setStartSetting(AutoStartSetting.EagerActive);
				}
				//TODO: what to do if storage persist failed 
			}
			if((state & Bundle.RESOLVED) != 0){
				boolean resolved = resolve() ; 
				if(!resolved){
					throw new BundleException("bundle cannot be resolved");
				}
			}
			if((options & Bundle.START_ACTIVATION_POLICY) != 0){
				if(((state & Bundle.STARTING)!=0) && lazyStarting){
					return ; 
				}
				state = Bundle.STARTING ; 
				lazyStarting = true ; 
				publishBundleEvent(BundleEvent.LAZY_ACTIVATION);
				waitForActivationTrigged();
			}
			state = Bundle.STARTING ; 
			startedAndStoppedCondition = lock.newCondition() ; 
			publishBundleEvent(BundleEvent.STARTING);
			try{
				activator = constructBundleActivator() ; 
				if(activator!=null){
					doActivate() ; 
				}
			}catch (Exception e) {
				state = Bundle.STOPPING ; 
				publishBundleEvent(BundleEvent.STOPPING);
				handleActivateException(e);
				state = Bundle.RESOLVED ; 
				publishBundleEvent(BundleEvent.STOPPED);
				throw new BundleException("activate exception");
			}finally{
				startedAndStoppedCondition.signalAll() ; 
			}
			if(state == Bundle.UNINSTALLED){
				throw new BundleException("uninstalled when activate");
			}
			state = Bundle.ACTIVE ; 
			publishBundleEvent(BundleEvent.STARTED) ; 

		}else{
			if((options&Bundle.START_TRANSIENT)!=0){
				throw new BundleException("bundle cannot be started due to the Framework’s current start level");
			}else{
				if((options&Bundle.START_ACTIVATION_POLICY)!=0){
					storage.setStartSetting(AutoStartSetting.DeclaredActive);
				}else{
					storage.setStartSetting(AutoStartSetting.EagerActive);
				}
			}
		}
		
	}

	private void handleActivateException(Exception e) {
		// TODO Auto-generated method stub
		
	}

	private void doActivate() {
		// TODO Auto-generated method stub
		
	}

	private BundleActivator constructBundleActivator() {
		// TODO Auto-generated method stub
		return null;
	}

	private void waitForActivationTrigged() {
		classLoader.registerListener(new BundleLoader.LoadListener() {
			public void onFirstClassLoaded() {
				// TODO Auto-generated method stub
			}
		});
	}

	private boolean resolve() {
		// TODO Auto-generated method stub
		return false;
	}

	private void publishBundleEvent(int started) {
		// TODO Auto-generated method stub
		
	}

	private boolean startLevelStatisfied() {
		//need to extend start level service to implement this method 
		return false;
	}

	public void start() throws BundleException {
		start(0);
	}

	public void stop(int options) throws BundleException {

		/**
		 * p 6.1.4.33
		 */
		if(state==Bundle.UNINSTALLED){
			throw new IllegalStateException("uninstalled bundle cannot be stopped");
		}

		if((((state & Bundle.STARTING)!=0)&&(!lazyStarting))||
				((state & Bundle.STOPPING)!=0)){
			boolean timeout = false ; 
			try{
				//TODO： need wait in a while ? 
				timeout = startedAndStoppedCondition.await(30000, TimeUnit.MILLISECONDS);
			}catch (InterruptedException e) {
				// TODO: handle exception
			}
			if(timeout){
				throw new BundleException("Bundle stop time out.");
			}
		}
		if((options & Bundle.START_TRANSIENT)!=0){
			storage.setStartSetting(AutoStartSetting.Stopped);
			//TODO: what to do if storage persist failed 
		}
		if(!((((state & Bundle.STARTING)!=0) && lazyStarting)||((state & Bundle.ACTIVE)!=0))){ //lazy starting 
			return ; 
		}
		state = Bundle.STOPPING ; 
		startedAndStoppedCondition = lock.newCondition() ; 
		Exception deactiveException = null ; 
		try{
			if(activator!=null){
				doDeactivate() ; 
			}
		}catch (Exception e) {
			deactiveException = e ; 
		}
		try{
			doStopAction();
			if(deactiveException!=null){
				throw new BundleException("deactive exception", deactiveException);
			}
			if(state == Bundle.UNINSTALLED){
				throw new BundleException("uninstalled when activate");
			}
			state = Bundle.RESOLVED ; 
			
		}finally{
			startedAndStoppedCondition.signalAll() ; 
		}
		publishBundleEvent(BundleEvent.STOPPED) ; 
	}

	private void doStopAction() {
		// TODO Auto-generated method stub
	}

	private void doDeactivate() {
		// TODO Auto-generated method stub
	}

	public void stop() throws BundleException {
		stop(0);
	}

	public void update(InputStream input) throws BundleException {
		// TODO Auto-generated method stub

	}

	public void update() throws BundleException {
		// TODO Auto-generated method stub

	}

	public void uninstall() throws BundleException {
		// TODO Auto-generated method stub

	}

	public Dictionary<String, String> getHeaders() {
		return metadata.getHeaders();
	}

	public long getBundleId() {
		return storage.getBundleId();
	}

	public String getLocation() {
		return storage.getLocation();
	}

	public ServiceReference<?>[] getRegisteredServices() {
		// TODO Auto-generated method stub
		return null;
	}

	public ServiceReference<?>[] getServicesInUse() {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean hasPermission(Object permission) {
		// TODO Auto-generated method stub
		return false;
	}

	public URL getResource(String name) {
		return classLoader.getResource(name);
	}

	public Dictionary<String, String> getHeaders(String locale) {
		return metadata.getHeaders(locale);
	}

	public String getSymbolicName() {
		return metadata.getSymbolicName();
	}

	public Class<?> loadClass(String name) throws ClassNotFoundException {
		return classLoader.loadClass(name);
	}

	public Enumeration<URL> getResources(String name) throws IOException {
		return classLoader.getResources(name);
	}

	public Enumeration<String> getEntryPaths(String path) {
		return resources.getEntryPaths(path);
	}

	public URL getEntry(String path) {
		return resources.getEntry(path);
	}

	public long getLastModified() {
		return storage.getLastModifyTime();
	}

	public Enumeration<URL> findEntries(String path, String filePattern,
			boolean recurse) {
		return resources.findEntries(path, filePattern, recurse);
	}

	public BundleContext getBundleContext() {
		return context;
	}

	public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(
			int signersType) {
		// TODO Auto-generated method stub
		return null;
	}

	public Version getVersion() {
		return metadata.getVersion();
	}

	public <A> A adapt(Class<A> type) {
		// TODO still not know
		return null;
	}

	public File getDataFile(String filename) {
		return storage.getDataFile();
	}
	
	

}
